#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
    Module Documentation
    here
"""

# Created by  : Zhang Chengdong
# Create Date : 2024/11/22 13:58
# Version = v0.1.0

__author__ = "Zhang Chengdong"
__copyright__ = "Copyright 2024. Large scale model"
__credits__ = ['Zhang Chengdong']

__liscence__ = "MIT"
__version__ = "1.0.1"
__maintainer__ = "Zhang Chengdong"
__status__ = "Production"

import os
import shap
import json
import time
import glob
import logging
import joblib
import optuna
import logging
import seaborn as sns
import numpy as np
import pandas as pd
from datetime import datetime
from matplotlib import pyplot as plt
from typing import Tuple, Union
from functools import partial
from collections import defaultdict
from django.conf import settings
from lightgbm import LGBMRegressor
from catboost import CatBoostRegressor, Pool

from sklearn.metrics import r2_score, mean_squared_error
from sklearn.model_selection import cross_val_score, train_test_split, KFold

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

plt.rcParams['font.sans-serif'] = ['SimHei']  # 用黑体显示中文
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题


class NonLinearModel():
    """
    """

    def __init__(self, data: pd.DataFrame, save_model_path: str, model_name: str, mas_model_type: str):
        self.data = data
        self.model_name = model_name
        self.mas_model_type = mas_model_type
        self.save_model_path = os.path.join(save_model_path, self.model_name)  # 模型保存的路径
        self.model_filename = None  # 模型名称（根据时间自命名）
        self.thresher = None
        self.target_1d = '水泥1天实测值'
        self.target_3d = "水泥3天实测值"
        self.target_28d = "水泥28天实测值"
        self.model_type = None
        feature_col = self.data.columns.tolist()
        remove_list = ['磨号', '品种', "时间"]
        feature_col = list(set(feature_col) - set(remove_list))
        target_list = [self.target_1d, self.target_3d, self.target_28d]
        self.feature = list(set(feature_col) - set(target_list))

    def object_tive_old(self, trial, x_train, y_train):
        """
        超参数配置，并加入到模型中
        :param trial:
        :param x_train:
        :param y_train:
        :return:s
        """
        model_name = self.model_name

        # 定义模型及其参数
        if model_name == 'CatBoost':
            params = {
                'thread_count': 10,
                'iterations': 10,
                'learning_rate': trial.suggest_loguniform('learning_rate', 0.001, 0.1),
                'depth': trial.suggest_int('depth', 3, 10),
                # 'l2_leaf_reg': trial.suggest_float('l2_leaf_reg', 1, 20, log=True),
                "verbose": 0,
            }
            params.update(settings.train_optuna)
        elif model_name == "LightGBM":
            params = {
                'boosting_type': 'gbdt',
                'objective': 'regression',
                'num_leaves': 31,
                'learning_rate': trial.suggest_loguniform('learning_rate', 0.01, 0.1),
                'verbose': 0,
                'max_depth': trial.suggest_int('max_depth', 5, 10)
            }
        model = settings.MODEL_DICT[self.model_name](**params)
        kf = KFold(n_splits=5, shuffle=True, random_state=42)
        if model_name == 'CatBoost':
            scores = cross_val_score(model, x_train, y_train, cv=kf, scoring='neg_mean_squared_error',
                                     fit_params={'cat_features': ['月份']})
        else:
            scores = cross_val_score(model, x_train, y_train, cv=kf, scoring='neg_mean_squared_error')
        score = np.mean(scores)
        return -score

    def object_tive(self, trial, x_train, y_train):
        """
        超参数配置，并加入到模型中
        :param trial:
        :param x_train:
        :param y_train:
        :return:s
        """
        model_name = self.model_name

        # 定义模型及其参数
        if model_name == 'CatBoost':
            params = {
                'thread_count': 10,
                'iterations': 10,
                'learning_rate': trial.suggest_loguniform('learning_rate', 0.001, 0.1),
                'depth': trial.suggest_int('depth', 3, 10),
                # 'l2_leaf_reg': trial.suggest_float('l2_leaf_reg', 1, 20, log=True),
                "verbose": 0,
            }
            params.update(settings.train_optuna)
        elif model_name == "LightGBM":
            params = {
                'boosting_type': 'gbdt',
                'objective': 'regression',
                'num_leaves': 31,
                'learning_rate': trial.suggest_loguniform('learning_rate', 0.01, 0.1),
                'verbose': 0,
                'max_depth': trial.suggest_int('max_depth', 5, 10)
            }
        model = settings.MODEL_DICT[self.model_name](**params)
        kf = KFold(n_splits=5, shuffle=True, random_state=42)
        if model_name == 'CatBoost':
            scores = cross_val_score(model, x_train, y_train, cv=kf, scoring='neg_mean_squared_error')
        else:
            scores = cross_val_score(model, x_train, y_train, cv=kf, scoring='neg_mean_squared_error')
        rmse_score = np.sqrt(-scores)
        return np.mean(rmse_score)

    def deal_na_data(self, data: pd.DataFrame, columns_name_list: list):
        """
        :param data:
        :param columns_name:
        :return:
        """
        return data.dropna(subset=columns_name_list, axis=0)

    def get_best_parameter(self, x_train, y_train, feature_name):
        """
        利用5折叠获取最优超参数
        :param x_train:
        :param y_train:
        :param feature_name:
        :return:
        """
        study = optuna.create_study(direction="minimize")
        partial_object = partial(self.object_tive, x_train=x_train, y_train=y_train)
        study.optimize(partial_object, n_trials=100)
        best_params = study.best_params
        best_value = study.best_value
        print("最优参数:", best_params)
        print("最优值:", best_value)

        return study.best_trial.params

    def save_model_to_pkl(self, model, mse, r2, feature_names, target_name, cat_features="月份", best_pass_rate=None,
                          model_type="model3_1d"):
        """

        :param model:
        :param feature_names:
        :param target_name:
        :param cat_features:
        :param best_pass_rate:
        :param model_type:
        :return:
        """
        # 保存模型到本地文件
        if not os.path.exists(self.save_model_path):
            os.makedirs(self.save_model_path)
        self.model_filename = os.path.join(self.save_model_path,
                                           '{}_{}.pkl'.format(self.mas_model_type, model_type))
        if self.model_name == "CatBoost":
            model_info = {
                'model': model,
                'feature_names': feature_names,
                'target_name': target_name,
                'cat_features': cat_features,
                'model_name': model_type,
                "mse": mse,
                "r2": r2,
                "pass_rate": best_pass_rate
            }
        elif self.model_name == "LightGBM":
            model_info = {
                'model': model,
                'feature_names': feature_names,
                'target_name': target_name,
                'model_name': model_type
            }
        joblib.dump(model_info, self.model_filename)
        print(f"模型已保存到 {self.model_filename}")

    def optimal_parameters(self, model_parameters: dict, save_para_for_model_path: str):
        """
        将最优参数进行保存
        :param model_parameters:
        :param save_para_for_model_path:
        :return:
        """
        if not os.path.exists(self.save_model_path):
            os.makedirs(self.save_model_path)
        save_para_for_model = save_para_for_model_path
        with open(save_para_for_model, "w", encoding="utf-8") as f:
            json.dump(model_parameters, f, indent=4)
        print(f"模型参数已经保存到 {self.model_filename}")

    def load_model_parameters(self, path: str):
        """
        加载参数
        :param path:
        :return:
        """
        with open(path, "r", encoding="utf-8") as f:
            model_parameters = json.load(f)
        return model_parameters

    def test_and_eval(self, final_model, x_test, y_test, feature: list = None, tolerance: float = 0.5):
        """
        多个样本进行预测
        :param final_model:
        :param x_test:
        :param y_test:
        :param feature:
        :param tolerance:
        :return:
        """
        y_pred = final_model.predict(x_test)
        mse = mean_squared_error(y_test, y_pred)
        r2 = r2_score(y_test, y_pred)
        # 计算合格率
        # 计算预测值与真实值之间的差异，并统计合格的样本比例
        tolerance = tolerance
        within_tolerance = np.abs(y_pred - y_test) <= tolerance
        percentage_within_tolerance = np.mean(within_tolerance) * 100
        logging.info(f"MSE: {mse}")
        logging.info(f"R2 Score: {r2}")
        logging.info(f"Percentage of predictions within ±{tolerance}: {percentage_within_tolerance:.2f}%")
        self.plt_all_local_image(x_test, self.model_filename, feature=feature)
        # self.plt_train_test_images(y_pred.tolist(), y_test.tolist(), within_tolerance.tolist(), mse, r2, tolerance,
        #                            percentage_within_tolerance)

    def load_model(self, model_filename: str = None):
        """
        加载模型
        :param model_filename:
        :return:
        """
        if model_filename is None:
            loaded_model = joblib.load(self.model_filename)
        else:
            self.model_filename = model_filename
            loaded_model = joblib.load(model_filename)
        return loaded_model

    def plt_train_test_images(self, y_pred, y_test, within_tolerance, mse, r2, tolerance, percentage_within_tolerance):
        """
        :param y_pred:
        :param y_test:
        :param within_tolerance:
        :param mse:
        :param r2:
        :param tolerance:
        :param percentage_within_tolerance:
        :return:
        """
        # 绘制折线图
        plt.figure(figsize=(10, 6))
        sns.lineplot(x=range(len(y_test)), y=y_test, label='Actual')
        sns.lineplot(x=range(len(y_pred)), y=y_pred, label='Predicted')

        # 标注误差
        for i in range(len(y_test)):
            plt.plot([i, i], [y_test[i] - self.thresher, y_test[i] + self.thresher], color='grey', linestyle='--',
                     linewidth=0.5)
            if within_tolerance[i]:
                plt.text(i, y_test[i], f'{y_pred[i]:.2f}', color='black', ha='center', va='bottom')
            else:
                plt.text(i, y_test[i], f'{y_pred[i]:.2f}', color='red', ha='center', va='bottom')

        plt.xlabel('Sample Index')
        plt.ylabel('Value')
        plt.title(
            f"MSE: {mse}\nR2 Score: {r2}\n Percentage of predictions within ±{tolerance}: {percentage_within_tolerance:.2f}%")
        plt.legend()

        # 保存图像
        image_path = os.path.splitext(self.model_filename)[0]  # 获取模型文件名（不带扩展名）
        plt.savefig("{}_test.png".format(image_path), dpi=300, bbox_inches='tight')

        # 显示图表
        plt.show()

    def plt_all_local_image(self, x_test, model_filename: str = None, feature: list = None):
        """
        画图
        :param x_test:
        :param model_filename:
        :param feature:
        :return:
        """
        if feature is None:
            feature = self.feature
        loaded_model = self.load_model(model_filename)['model']
        # 使用 SHAP 可视化模型权重
        explainer = shap.TreeExplainer(loaded_model)
        shap_values = explainer.shap_values(x_test)
        # 绘制全局特征重要性图
        img_name = str(int(time.time() * 1000)) + ".png"
        image_path = os.path.splitext(self.model_filename)[0]  # 获取模型文件名（不带扩展名）
        if not os.path.exists(image_path):
            os.makedirs(image_path)
        image_plt_path = os.path.join(image_path, img_name)
        try:
            shap.summary_plot(shap_values, x_test, feature_names=feature, show=False)
            plt.savefig(image_plt_path, bbox_inches='tight')
            plt.close()
        except Exception as e:
            print(e)

    def convert_month_to_category(self, data: pd.DataFrame, model_name: str) -> pd.DataFrame:
        """
        将 '月份' 列转换为类别编码，并根据模型名称决定是否执行转换。

        :param data: 输入的 DataFrame
        :param model_name: 模型名称，支持 'LightGBM' 或 'CatBoost'
        :return: 转换后的 DataFrame
        """
        if model_name == 'LightGBM':
            data['月份'] = data['月份'].astype('category').cat.codes
        return data

    def select_model_params_old(self, model_name: str, final_model: Union[LGBMRegressor, CatBoostRegressor], x_train,
                                y_train, pass_threshold=1.) -> Tuple:
        """
        根据模型名称选择模型参数并训练模型。

        :param model_name: 模型名称，支持 'LightGBM' 或 'CatBoost'
        :param final_model: 最终模型实例，可以是 LGBMRegressor 或 CatBoostRegressor
        :param x_train: 训练数据的特征
        :param y_train: 训练数据的目标值
        :param pass_threshold: 阈值信息
        :return: 训练后的模型实例
        """
        if model_name == "LightGBM":

            final_model.fit(x_train, y_train)
            return final_model, None, None

        elif model_name == "CatBoost":
            best_model = None
            best_score = float("inf")
            best_mse = None
            best_r2 = None
            best_pass_rate = None
            kf = KFold(n_splits=2, shuffle=True)
            for fold, (train_index, val_index) in enumerate(kf.split(x_train)):
                x_train_fold, x_val_fold = x_train.iloc[train_index], x_train.iloc[val_index]
                y_train_fold, y_val_fold = y_train.iloc[train_index], y_train.iloc[val_index]
                train_pool = Pool(x_train_fold, y_train_fold, cat_features=['月份'])
                val_pool = Pool(x_val_fold, y_val_fold, cat_features=['月份'])
                final_model.fit(train_pool, eval_set=val_pool, early_stopping_rounds=50)
                val_score = final_model.get_best_score()['validation']['RMSE']
                # 计算验证集上的 MSE 和 R²
                y_val_pred = final_model.predict(val_pool)
                mse = mean_squared_error(y_val_fold, y_val_pred)
                r2 = r2_score(y_val_fold, y_val_pred)

                # 合格率
                abs_errors = np.abs(y_val_fold - y_val_pred)
                pass_rate = np.mean(abs_errors < pass_threshold)
                # 保存最佳模型
                if val_score < best_score:
                    best_score = val_score
                    best_mse = mse
                    best_r2 = r2
                    best_pass_rate = pass_rate
                    best_model = final_model.copy()
            return best_model, best_mse, best_r2, best_pass_rate

    def select_model_params(self, model_name: str, final_model: Union[LGBMRegressor, CatBoostRegressor], x_train,
                            y_train, pass_threshold=1., x_test=None, y_test=None) -> Tuple:
        """
        根据模型名称选择模型参数并训练模型。

        :param model_name: 模型名称，支持 'LightGBM' 或 'CatBoost'
        :param final_model: 最终模型实例，可以是 LGBMRegressor 或 CatBoostRegressor
        :param x_train: 训练数据的特征
        :param y_train: 训练数据的目标值
        :param pass_threshold: 阈值信息
        :param x_test:
        :param y_test:
        :return: 训练后的模型实例
        """
        if model_name == "LightGBM":

            final_model.fit(x_train, y_train)
            return final_model, None, None

        elif model_name == "CatBoost":
            final_model.fit(x_train, y_train)
            y_pred = final_model.predict(x_test)
            mse = mean_squared_error(y_test, y_pred)
            r2 = r2_score(y_test, y_pred)
            tolerance = pass_threshold
            within_tolerance = np.abs(y_pred - y_test) <= tolerance
            percentage_within_tolerance = np.mean(within_tolerance) * 100
            return final_model, round(mse, 2), round(r2, 2), round(percentage_within_tolerance, 2)

    def train_use_kfold(self, model_class, best_params, x_train, y_train):
        """
        用来做k折叠验证找最优模型
        :param model_class:
        :param best_params:
        :param x_train:
        :param y_train:
        :return:
        """
        # 定义 K 折交叉验证的折数
        k = 5  # 例如，5 折交叉验证
        kf = KFold(n_splits=k, shuffle=True, random_state=42)

        # 初始化变量来保存最优模型的分数和模型对象
        best_score = np.inf
        best_model = None

        # 进行 K 折交叉验证并保存最优模型
        for fold, (train_index, val_index) in enumerate(kf.split(x_train)):
            print(f"Training on fold {fold + 1}/{k}")

            # 获取训练和验证数据
            x_train_fold, x_val_fold = x_train.iloc[train_index], x_train.iloc[val_index]
            y_train_fold, y_val_fold = y_train.iloc[train_index], y_train.iloc[val_index]

            # 训练模型
            model = model_class(**best_params)
            model.fit(x_train_fold, y_train_fold)

            # 在验证集上进行预测并计算分数
            y_val_pred = model.predict(x_val_fold)
            val_score = np.mean((y_val_pred - y_val_fold) ** 2)  # 计算均方误差

            # 如果当前模型的分数优于之前的最佳分数，则更新最佳模型
            if val_score < best_score:
                best_score = val_score
                best_model = model
        return best_model

    def train_model_1d(self):
        """
        训练1天模型
        :return:
        """
        feature = self.feature.copy()
        data = self.data.copy()
        self.model_type = "1"
        model_type = "model1"
        data = self.deal_na_data(data, columns_name_list=[self.target_1d])
        data = self.convert_month_to_category(data, model_name=self.model_name)
        data = data[data[self.target_1d] >= 5.]
        data = data.reset_index(drop=True)
        logging.info("训练1天水泥实测模型，使用的数据量为：{} ".format(data.shape[0]))
        feature.remove("月份")
        # 做数据分割
        x = data[feature]
        y = data[self.target_1d]
        # 分割数据集
        x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
        save_para_for_model = os.path.join(self.save_model_path,
                                           "{}_optimal_parameters.json".format(model_type))
        self.thresher = 1.
        model_class = settings.MODEL_DICT[self.model_name]
        if not os.path.exists(save_para_for_model):
            best_params = self.get_best_parameter(x_train, y_train, feature)
            best_params.update(settings.train_optuna)
            self.optimal_parameters(best_params, save_para_for_model)
            final_model = model_class(**best_params)
            final_model, best_mse, best_r2, best_pass_rate = self.select_model_params(model_name=self.model_name,
                                                                                      final_model=final_model,
                                                                                      x_train=x_train,
                                                                                      y_train=y_train,
                                                                                      pass_threshold=1.,
                                                                                      x_test=x_test, y_test=y_test)
            self.save_model_to_pkl(final_model, best_mse, best_r2, feature, self.target_1d, cat_features='月份',
                                   best_pass_rate=best_pass_rate,
                                   model_type=model_type)
            self.test_and_eval(final_model, x_test, y_test, feature=feature,
                               tolerance=self.thresher)  # TODO 如果需要r2和mas的话需要重写这个方法

        else:
            best_params = self.load_model_parameters(save_para_for_model)
            final_model = model_class(**best_params)
            final_model, best_mse, best_r2, best_pass_rate = self.select_model_params(model_name=self.model_name,
                                                                                      final_model=final_model,
                                                                                      x_train=x_train,
                                                                                      y_train=y_train,
                                                                                      pass_threshold=1.,
                                                                                      x_test=x_test, y_test=y_test)
            self.save_model_to_pkl(final_model, best_mse, best_r2, feature, self.target_1d, cat_features='月份',
                                   best_pass_rate=best_pass_rate,
                                   model_type=model_type)
            self.test_and_eval(final_model, x_test, y_test, feature=feature,
                               tolerance=self.thresher)  # TODO 如果需要r2和mas的话需要重写这个方法
        return best_mse, best_r2
        # self.test_and_eval(final_model, x_test, y_test, feature=feature, tolerance=self.thresher) # TODO 如果需要r2和mas的话需要重写这个方法

    def train_model_3d(self, actual1d: bool = True):
        """
        训练3天模型
        :param actual1d:
        :return:
        """
        self.model_type = "3"
        if actual1d:
            model_type = settings.model_type_suffix["model3_1d"]
        else:
            model_type = settings.model_type_suffix["model3_n1d"]

        feature = self.feature.copy()
        feature.remove("月份")
        data = self.data.copy()
        data = data[data[self.target_3d] >= 20]
        if actual1d:
            feature.append(self.target_1d)
        data = self.deal_na_data(data, columns_name_list=[self.target_3d])
        if data.shape[0] <= 10:
            logging.info("=== 训练3天水泥实测模型，能够进行模型训练的数据量为：{}，不足以完成非线性模型训练 ===".format(
                data.shape[0]))
            return None, None
        data = self.convert_month_to_category(data, model_name=self.model_name)
        data = data.reset_index(drop=True)
        logging.info("训练3天水泥实测模型，使用的数据量为：{} ".format(data.shape[0]))
        # 做数据分割
        x = data[feature]
        y = data[self.target_3d]
        # 分割数据集
        x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)

        save_para_for_model = os.path.join(self.save_model_path,
                                           "{}_optimal_parameters.json".format(model_type))
        self.thresher = 1.2
        model_class = settings.MODEL_DICT[self.model_name]
        if not os.path.exists(save_para_for_model):
            best_params = self.get_best_parameter(x_train, y_train, feature)
            best_params.update(settings.train_optuna)
            self.optimal_parameters(best_params, save_para_for_model)
            final_model = model_class(**best_params)
            final_model, best_mse, best_r2, best_pass_rate = self.select_model_params(model_name=self.model_name,
                                                                                      final_model=final_model,
                                                                                      x_train=x_train,
                                                                                      y_train=y_train,
                                                                                      pass_threshold=1.2,
                                                                                      x_test=x_test, y_test=y_test)
            self.save_model_to_pkl(final_model, best_mse, best_r2, feature, self.target_3d, cat_features='月份',
                                   best_pass_rate=best_pass_rate,
                                   model_type=model_type)
            self.test_and_eval(final_model, x_test, y_test, feature=feature,
                               tolerance=self.thresher)  # TODO 如果需要r2和mas的话需要重写这个方法

        else:
            best_params = self.load_model_parameters(save_para_for_model)
            final_model = model_class(**best_params)
            final_model, best_mse, best_r2, best_pass_rate = self.select_model_params(model_name=self.model_name,
                                                                                      final_model=final_model,
                                                                                      x_train=x_train,
                                                                                      y_train=y_train,
                                                                                      pass_threshold=1.2,
                                                                                      x_test=x_test, y_test=y_test)
            self.save_model_to_pkl(final_model, best_mse, best_r2, feature, self.target_3d, cat_features='月份',
                                   best_pass_rate=best_pass_rate,
                                   model_type=model_type)
            self.test_and_eval(final_model, x_test, y_test, feature=feature,
                               tolerance=self.thresher)  # TODO 如果需要r2和mas的话需要重写这个方法
        return best_mse, best_r2

    def train_model_28d(self, actual1d: bool = False, actual3d: bool = False):
        """
        训练28天实测水泥强度
        :param actual1d:
        :param actual3d:
        :return:
        """
        self.model_type = "28"
        if actual1d:
            model_type = settings.model_type_suffix["model28_1d"]
        elif actual3d:
            model_type = settings.model_type_suffix["model28_3d"]
        else:
            model_type = settings.model_type_suffix["model28_n1d"]

        feature = self.feature.copy()
        feature.remove("月份")
        data = self.data.copy()
        if actual1d:
            feature.extend([self.target_1d])
        if actual3d:
            feature.extend([self.target_1d, self.target_3d])
        data = data[data[self.target_28d] >= 40]
        data = self.deal_na_data(data, columns_name_list=[self.target_28d])
        if data.shape[0] <= 10:
            logging.info("=== 训练28天水泥实测模型，能够进行模型训练的数据量为：{}，不足以完成非线性模型训练 ===".format(
                data.shape[0]))
            return None, None
        data = self.convert_month_to_category(data, model_name=self.model_name)
        data = data.reset_index(drop=True)
        logging.info("训练28天水泥实测模型，使用的数据量为：{} ".format(data.shape[0]))
        # 做数据分割
        x = data[feature]
        y = data[self.target_28d]
        # 分割数据集
        x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
        save_para_for_model = os.path.join(self.save_model_path,
                                           "{}_optimal_parameters.json".format(model_type))
        self.thresher = 1.5
        model_class = settings.MODEL_DICT[self.model_name]
        if not os.path.exists(save_para_for_model):
            best_params = self.get_best_parameter(x_train, y_train, feature)
            best_params.update(settings.train_optuna)
            self.optimal_parameters(best_params, save_para_for_model)
            final_model = model_class(**best_params)
            final_model, best_mse, best_r2, best_pass_rate = self.select_model_params(model_name=self.model_name,
                                                                                      final_model=final_model,
                                                                                      x_train=x_train,
                                                                                      y_train=y_train,
                                                                                      pass_threshold=1.5,
                                                                                      x_test=x_test, y_test=y_test)
            self.save_model_to_pkl(final_model, best_mse, best_r2, feature, self.target_28d, cat_features='月份',
                                   best_pass_rate=best_pass_rate,
                                   model_type=model_type)
            self.test_and_eval(final_model, x_test, y_test, feature=feature,
                               tolerance=self.thresher)  # TODO 如果需要r2和mas的话需要重写这个方法

        else:
            best_params = self.load_model_parameters(save_para_for_model)
            # best_params.update(settings.train_optuna)
            final_model = model_class(**best_params)
            final_model, best_mse, best_r2, best_pass_rate = self.select_model_params(model_name=self.model_name,
                                                                                      final_model=final_model,
                                                                                      x_train=x_train,
                                                                                      y_train=y_train,
                                                                                      pass_threshold=1.5,
                                                                                      x_test=x_test, y_test=y_test)
            self.save_model_to_pkl(final_model, best_mse, best_r2, feature, self.target_28d, cat_features='月份',
                                   best_pass_rate=best_pass_rate,
                                   model_type=model_type)
            self.test_and_eval(final_model, x_test, y_test, feature=feature,
                               tolerance=self.thresher)  # TODO 如果需要r2和mas的话需要重写这个方法
        return best_mse, best_r2
